type binop =
	| Badd | Bsub | Bmul | Bdiv | Bmod | Blt | Ble | Bgt | Bge | Bequals | Bnotequals | Band | Bor | Bsemicolon

type unop =
	| Uplus | Uminus | Unot

type expression =
	| Constint of int
	| Conststring of string
	| Constbool of bool
	| Var of string
	| Class of string
	| Binop of binop * expression * expression
	| Unop of unop * expression
	| New of string
	| Instanceof of expression * string
	| Cast of string * expression
	| Get of expression * string
	| Call of expression * string * arg_definitions
	| Set of expression * expression
	| Declare of string * string * expression * expression
	| If of expression * expression * expression
	| NoExpression
	| Null
and arg_definitions = 
	| Args of expression * arg_definitions
	| NoArg

type param_definition = 
	| Param of string * string

type param_definitions = 
	| Params of param_definition * param_definitions
	| NoParam

type class_declaration =
	| Attributedef of string * string * expression
	| Methodedef of string * string * param_definitions * expression

type class_declarations =
	| Classdecl of class_declaration * bool * class_declarations
	| Nodeclaration

type file_element =
	| Classdef of string * string * class_declarations
	| MainExpr of expression

type file_elements =
	| FileElements of file_element * file_elements
	| Empty


let string_of_op_u = function
	| Uplus -> "+"
	| Uminus -> "-"
	| Unot -> "!"

let string_of_op_b = function
	| Badd -> "+"
	| Bsub -> "-"
	| Bmul -> "*"
	| Bdiv -> "/"
	| Bmod -> "%"
	| Blt -> "<"
	| Ble -> "<="
	| Bgt -> ">"
	| Bge -> ">="
	| Bequals -> "=="
	| Bnotequals -> "!="
	| Band -> "&&"
	| Bor-> "||"
	| Bsemicolon-> ";"

let rec string_of_expr exp =
	match exp with
		| Constint c -> string_of_int c
		| Conststring s -> "\""^s^"\""
		| Constbool b -> string_of_bool b
		| Var v -> v
		| Class s -> s
		| Binop(Bsemicolon, e1, e2) ->
				"{" ^(string_of_expr e1)^";\n"^(string_of_expr e2)^ "}"
		| Binop(op, e1, e2) ->
				"(" ^(string_of_expr e1)^(string_of_op_b op) ^(string_of_expr e2)^ ")"
		| Unop(op, e) -> "(" ^ (string_of_op_u op) ^(string_of_expr e)^ ")"
	  | New s -> "new "^s
	  | Instanceof(e, s) -> "("^(string_of_expr e)^") instanceof ("^s^")"
	 	| Cast(s, e) ->  "("^s^")("^(string_of_expr e)^")"
		| Get(e, s) -> "("^(string_of_expr e)^")."^s
		| Call(e, s, args) -> "("^(string_of_expr e)^")."^s^"("^(string_of_args args)^")"
		| Set(e1, e2) -> (string_of_expr e1)^" = "^(string_of_expr e2)
		| Declare(s1, s2, e1, e2) -> "let "^s2^" of type "^s1^" = "^(string_of_expr e1)^" in {\n"^(string_of_expr e2)^"\n}"
		| If(e1, e2, e3) -> "if("^(string_of_expr e1)^") {\n"^(string_of_expr e2)^"\n} else {\n"^(string_of_expr e3)^"\n}"
		| Null -> "null"
		| NoExpression -> "--unit--"
and string_of_args args =
	match args with
	| Args(e, NoArg) -> string_of_expr e
	| Args(e, args) -> (string_of_expr e)^", "^(string_of_args args)
	| NoArg -> ""


let string_of_param_definition param_definition =
	match param_definition with
	| Param(classname, name) -> classname^" "^name

let rec string_of_param_definitions param_definitions =
	match param_definitions with
	| Params(param_def, NoParam) -> (string_of_param_definition param_def)
	| Params(param_def, param_defs) -> (string_of_param_definition param_def)^", "^(string_of_param_definitions param_defs)
	| NoParam -> ""

let rec string_of_class_declaration class_declaration =
	match class_declaration with
		| Attributedef(classname, name, value) -> "attributedef type=\""^classname^"\" name=\""^name^"\" value={\n"^(string_of_expr value)^"\n}"
		| Methodedef(classname, name, params, value) -> "methoddef type=\""^classname^"\" name=\""^name^"\" params=["^(string_of_param_definitions params)^"] value={\n"^(string_of_expr value)^"\n}"

let rec string_of_class_declarations class_declarations =
	match class_declarations with
		| Classdecl(classdecl, isstatic, classdecls) -> (if isstatic then "static " else "")^(string_of_class_declaration classdecl)^"\n"^(string_of_class_declarations classdecls)
		| Nodeclaration -> ""

let rec string_of_file_element file_element =
	match file_element with
		| Classdef(name, mother, classdecls) -> "classdef name=\""^name^"\" extends=\""^mother^"\" {\n"^string_of_class_declarations(classdecls)^"}"
		| MainExpr(e) -> string_of_expr(e)

let rec string_of_file_elements file_elements =
	match file_elements with
		| FileElements(fe, fes) -> (string_of_file_element fe)^"\n\n\n"^(string_of_file_elements fes)
		| Empty -> ""


	